package com.googlecode.mailnet.simulation;

import com.googlecode.mailnet.netmodel.QueueingConfiguration;

import java.util.Arrays;

/**
 * @author Iskander Akishev
 */
class GradatedMessageQueues {

	private final QueueingConfiguration queueingConfiguration;

	private final int n;
	private final BoundedMessageQueue[] queues;
	private final int[] points;

    GradatedMessageQueues(QueueingConfiguration queueingConfiguration) {
		this.queueingConfiguration = queueingConfiguration;
		this.n = queueingConfiguration.getQueuesNumber();
		this.queues = new BoundedMessageQueue[n];
		this.points = new int[n];
		for (int i = 0; i < queues.length; i++) {
			queues[i] = new BoundedMessageQueue(queueingConfiguration.getQueueLimit(i));
		}
	}

	boolean offerMessage(Message message) {
        bytesAttemptedToSend += message.size;
		return queues[message.priority].offer(message);
	}

	// todo: try to document it somehow...
	Message pollMessage() {
		// Detecting the group 
		int l = -1;
		for (int i = 0; i < n; i++) {
			if (!queues[i].isEmpty()) {
				l = i;
				break;
			}
		}
		if (l == -1) {
			// all queues are empty
			return null;
		}
		int r = l;
		while (r < n - 1 && !queueingConfiguration.isStrictlyPrioritized(r)) {
			r++;
		}
		// interesting group is from l to r inclisively;

		int timesToDistributePoints = Integer.MAX_VALUE;
		int j = -1;
		for (int i = l; i <= r; i++) {
			if (queues[i].isEmpty()) {
				continue;
			}
			int shortage = queues[i].peek().size - points[i];
			int current = shortage > 0 ?
				((shortage - 1) / queueingConfiguration.getQueueWeight(i)) + 1 :
				0;
			if (timesToDistributePoints > current) {
				timesToDistributePoints = current;
				j = i;
				if (timesToDistributePoints == 0) {
					break;
				}
			}
		}

		if (timesToDistributePoints > 0) {
			for (int i = l; i <= r; i++) {
				if (!queues[i].isEmpty()) {
					points[i] += timesToDistributePoints * queueingConfiguration.getQueueWeight(i);
				}
			}
		}

		assert !queues[j].isEmpty();
		assert queues[j].peek().size >= points[j];

		Message message = queues[j].poll();
		if (queues[j].isEmpty()) {
			points[j] = 0;
		} else {
			points[j] -= message.size;
		}
		
		return message;
	}

	public void dropAllMessages(NodeState location) {
		for (BoundedMessageQueue queue : queues) {
			queue.dropAllMessages(location);
		}
		Arrays.fill(points, 0);
	}

    // ==================== Statistics counters ====================

    int bytesAttemptedToSend;

    void clearStatistics() {
        bytesAttemptedToSend = 0;
    }
}
